今天大概會聊到的範圍
- ComposeView
之前有介紹過 Compose 中的 AndroidView ,是一個可以在 Compose 中放入一般 Android View 。那如果我們要在一般的 Android 專案中放入 Compose 呢? 有幾個方法可以在原先 Android 的專案中加入 Compose
之前有介紹過 Compose compiler 的運作機制,那些轉換都是透過 Kotlin 在 1.5 新增的 JVM IR compiler backend 。另外,Android Studio 也只有在 Arctic Fox 之後的版本才有支援 Compose、Android Build Tool 也需要在 7.0 以上:
// project-level build.gradle
buildscript {
...
dependencies {
classpath "com.android.tools.build:gradle:7.0.0"
...
}
}
// module-level build.gradle
android {
...
kotlinOptions {
jvmTarget = '1.8'
useIR = true
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion compose_version
kotlinCompilerVersion '1.5.10'
}
...
}
準備就緒,開始導入 Compose 吧!先加入 Compose 相關的 library:
dependencies {
// Integration with activities
implementation 'androidx.activity:activity-compose:1.3.1'
// Compose Material Design
implementation 'androidx.compose.material:material:1.0.1'
// Animations
implementation 'androidx.compose.animation:animation:1.0.1'
// Tooling support (Previews, etc.)
implementation 'androidx.compose.ui:ui-tooling:1.0.1'
// Integration with ViewModels
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07'
// UI Tests
androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.0.1'
}
如果整個頁面都可以使用 Compose 來建制的話,我們可以用我們學習 Compose 以來常常見到的好朋友 setContent function 來加入 Composable:
// 記得要加入 'androidx.activity:activity-compose:1.3.1'
class InteropActivity : AppCompatActivity() { // <-- 一般的 AppCompactActivity 就有繼承 ComponentActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { // <-- setContent 是 compose 在 ComponentActivity 身上加上的 extension fucntion
ComposeScreen() // <--- 這裡就可以放 Composable 了
}
}
}
如果只有 Activity 中的某個部分想轉成 Compose,在 xml 中使用 ComposeView :
// in xml
<LinearLayout ...>
...
<androidx.compose.ui.platform.ComposeView
android:id="@+id/interopComposeView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
...
</LinearLayout>
// in activity
class InteropActivity : AppCompatActivity() {
private lateinit var binding: ActivityInteropBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityInteropBinding.inflate(layoutInflater) // <-- 這裡是用 ViewBinding。一般的 findViewById 也可以喔
val root = binding.root
binding.interopComposeView.setContent {
// composable // ComposeView 有 setContent function 可以放入 composable
}
setContentView(root)
}
}
沒特別設定的話,ComposeView 會在 該 View detach window 時觸發 dispose。但是這可能不是期望的行為,例如在 Fragment 中我們可能希望跟著 Fragment 的 Lifecycle。
ComposeView 有 setViewCompositionStrategy function 可以使用,調整 dispose 的策略:
就這樣,把 ComposeView 當成一般的 View 放在 Android 內,再配合之前研究到的 AndroidView 就可以慢慢將計有專案 migrate 到 Compose 上囉!
Reference: